package com.demo.task.manager

import android.content.Context
import android.hardware.usb.UsbDeviceConnection
import android.hardware.usb.UsbManager
import android.util.Log
import com.example.rwxian.ZNlauncher.USBSerial.driver.UsbSerialDriver
import com.example.rwxian.ZNlauncher.USBSerial.driver.UsbSerialPort
import com.example.rwxian.ZNlauncher.USBSerial.driver.UsbSerialProber
import com.example.rwxian.ZNlauncher.USBSerial.util.SerialInputOutputManager
import com.example.rwxian.ZNlauncher.USBSerial.util.UsbUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.io.ByteArrayOutputStream
import java.io.UnsupportedEncodingException
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.TimeUnit

object UsbSerialPortManager {
    private const val TAG = "UserSerialPortManager"
    private const val IDENTIFICATION = " USB-Serial Controller D" // 目标设备标识

    private const val BAUD_RATE = 115200 // 波特率
    private const val DATA_BITS = 8 // 数据位
    private const val STOP_BITS = UsbSerialPort.STOPBITS_1 // 停止位
    private const val PARITY = UsbSerialPort.PARITY_NONE // 奇偶校验
    private const val RETRY_TIME = 2_000L //重试时间间隔

    private lateinit var usbManager: UsbManager

    private var usbSerialPort: UsbSerialPort? = null

    private var isOpen = false

    private var inputOutputManager: SerialInputOutputManager? = null
    private var deviceConnection: UsbDeviceConnection? = null

    private val dataQueue by lazy {
        ArrayBlockingQueue<ByteArray>(1)
    }

    private val _receiveFlow = MutableSharedFlow<ByteArray>()
    val receiveFlow: MutableSharedFlow<ByteArray> = _receiveFlow


    fun init(context: Context) {
        this.usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager
    }

    fun isOpenDevice(): Boolean {
        return isOpen
    }

    suspend fun openDevice(success: suspend () -> Unit = {}) {
        if (isOpen) {
            success.invoke()
            return
        }

        while (true) {
            val drivers = UsbSerialProber.getDefaultProber().findAllDrivers(usbManager)
            if (drivers.isNullOrEmpty()) {
                delay(RETRY_TIME)
                continue
            }

            //获取驱动
            var driver: UsbSerialDriver? = null
            when {
                drivers.size > 1 -> {
                    for (serialDriver in drivers) {
                        val productName = serialDriver.device.productName
                        if (productName == IDENTIFICATION) {
                            driver = serialDriver
                        }
                    }
                }

                else -> driver = drivers[0]
            }

            usbSerialPort = driver?.ports?.get(0)
            if (driver == null || usbSerialPort == null) {
                delay(RETRY_TIME)
                continue
            }

            Log.e(TAG, "检查权限 -> ${usbManager.hasPermission(driver.device)}")
            deviceConnection = usbManager.openDevice(driver.device)
            runCatching {
                usbSerialPort?.open(deviceConnection)
                usbSerialPort?.setParameters(BAUD_RATE, DATA_BITS, STOP_BITS, PARITY)
            }.onFailure {
                Log.e(TAG, "openDevice: 打开串口", it)
            }

            if (usbSerialPort?.isOpen == true) {
                isOpen = true
                inputOutputManager = SerialInputOutputManager(
                    usbSerialPort,
                    ReceiveManager()
                ).apply {
                    start()
                }
                break
            }

            delay(RETRY_TIME)
        }
        success.invoke()
    }

    suspend fun write(bytes: ByteArray) {
        if (bytes.isEmpty() || !isOpen) {
            return
        }
        Log.e(TAG, "write: 写数据 ${usbSerialPort?.isOpen}")
        withContext(Dispatchers.IO) {
            runCatching {
                Log.i(TAG, "write: $bytes")
                usbSerialPort?.write(bytes, 0) // 写入数据
            }.onFailure {
                Log.e(TAG, "write: 写数据异常", it)
            }
        }

    }

    suspend fun writeData(bytes: ByteArray): ByteArray? {
        if (bytes.isEmpty() || !isOpen) {
            return null
        }
        Log.e(TAG, "writeData: 写数据 ${usbSerialPort?.isOpen}")
        return withContext(Dispatchers.IO) {
            var data: ByteArray? = null
            runCatching {
                dataQueue.clear()
                usbSerialPort?.write(bytes, 0) // 写入数据
                data = dataQueue.poll(5, TimeUnit.SECONDS)
            }.onFailure {
                Log.e(TAG, "writeData: 写数据异常", it)
            }
            data
        }
    }

    fun closeDevice() {
        runCatching {
            inputOutputManager?.stop()
            deviceConnection?.close()
            usbSerialPort?.close()
            isOpen = false
        }.onFailure {
            Log.e(TAG, "closeDevice: ", it)
        }
    }

    private class ReceiveManager : SerialInputOutputManager.Listener {
        var baos: ByteArrayOutputStream = ByteArrayOutputStream()
        override fun onNewData(data: ByteArray) {
            // 将接收到的数据写入缓冲区
            baos.write(data, 0, data.size)
            val readBufferForUsb = baos.toByteArray()

            // 处理完整的数据
            val completeData = bytes2string(readBufferForUsb)
            Log.e(TAG, "收到完整的 usb 数据: $completeData")
            val bytes = completeData.toByteArray()
            val s = UsbUtils.ByteArrayToHex(bytes)
            Log.e(TAG, "解析数据后：$s")
            baos.reset()
            runBlocking { _receiveFlow.emit(data) }
            dataQueue.offer(data)
        }

        override fun onRunError(e: Exception) {
            Log.e(TAG, "usb 断开了", e)
            isOpen = false
            closeDevice()
            runBlocking { openDevice() }
        }
    }

    fun bytes2string(bytes: ByteArray?): String {
        if (bytes == null) return ""

        var newStr = ""
        try {
            newStr = String(bytes, charset("GB18030")).trim { it <= ' ' }
        } catch (e: UnsupportedEncodingException) {
            e.printStackTrace()
        }
        return newStr
    }

}
